# Copyright (c) 2021-2023 Alibaba Cloud Computing Ltd.
# SPDX-License-Identifier: MulanPSL-2.0

import os
import logging

from agent.common.config import Config
from agent.common.system import sysCommand
from agent.domain.base import BaseDomain

from pynginxconfig import NginxConfig
from collections import defaultdict

logger = logging.getLogger('common')

KNOBS_BASE = [
    "worker_processes", 
    "worker_rlimit_nofile", 
    "worker_cpu_affinity"
]

KNOBS_EVENTS = [
    "worker_connections", 
    "multi_accept"
]

class Nginx(BaseDomain):
    __domain__ = "nginx_conf"
    
    def __init__(self):
        super().__init__(domain_name = self.__domain__)
        self.nginx_config_path = "/etc/nginx/nginx.conf"
        self.backup_file = os.path.join(Config.BACKUP_PATH, "nginx_backup.conf")
        self.nginx_conf = NginxConfig()

    def __append_param(self, param, item, position = 1):
        """ Append a parameter and value to nginx.conf

        """
        for index, element in enumerate(self.nginx_conf.data):
            if isinstance(element, dict) and element["name"] == param:
                self.nginx_conf.data[index]["value"].insert(position, item)
    
    def get_value(self, param_name):
        """ Read value of a parameter in nginx.conf
        """
        try:
            if param_name in KNOBS_BASE:
                return self.nginx_conf.get(param_name)[1]
            
            elif param_name in KNOBS_EVENTS:
                return self.nginx_conf.get([('events',), param_name])[1]
            
            else:
                return self.nginx_conf.get([('http',), param_name])[1]
            
        except Exception as e:
            logger.warning("fail to read '{}': {}".format(param_name, e))
            return None
    
    def set_value(self, param_name, param_value):
        """ Set value of a parameter in nginx.conf
        """
        try:
            param_value = "{}".format(param_value).strip()
            
            if param_name in KNOBS_BASE:
                if self.nginx_conf.get(param_name):
                    self.nginx_conf.set(param_name, param_value)
                else:
                    self.nginx_conf.append((param_name, param_value), position=4)
            
            elif param_name in KNOBS_EVENTS:
                if self.nginx_conf.get([("events",), param_name]):
                    self.nginx_conf.set([("events",), param_name], param_value)
                else:
                    self.__append_param("events", (param_name, param_value), 1)
            
            else:
                if self.nginx_conf.get([("http",), param_name]):
                    self.nginx_conf.set([("http",), param_name], param_value)
                else:
                    self.__append_param("http", (param_name, param_value), 7)
        
        except Exception as e:
            logger.error("fail to set '{}' to {}: {}".format(param_name, param_value, e))
            raise      

    def set_values(self, param_list: dict) -> dict:
        """ Set value of parameters.

        """
        self.nginx_conf.loadf(self.nginx_config_path)
        log_value = """main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"'"""
                    
        if self.nginx_conf.get([("http",), "log_format"]):
            self.nginx_conf.set([("http",), "log_format"], log_value)
            
        if self.nginx_conf.get([("http",), "access_log"]):
            self.nginx_conf.set([("http",), "access_log"], "off")

        res = defaultdict(dict)
        for param_name, param_info in param_list.items():
            try:
                self.set_value(param_name, param_info["value"])
                res[param_name] = {"suc": True, "value": param_info["value"], "msg": ""}
            
            except Exception as e:
                res[param_name] = {"suc":False, "value": "", "msg":"{}".format(e)}

        self.nginx_conf.savef(self.nginx_config_path)
        sysCommand("systemctl restart nginx")
        return res
    
    def get_values(self, param_list: dict) -> dict:
        """ Read value of parameters.

        """
        self.nginx_conf.loadf(self.nginx_config_path)
        res = defaultdict(dict)
        for param_name in param_list.keys():
            try:
                value = self.get_value(param_name)
                res[param_name] = {"suc":True, "value":str(value).replace("\t", " "), "msg":""}
                
            except Exception as e:
                res[param_name] = {"suc":False, "value": None, "msg":"{}".format(e)}
              
        return res
    
    def backup(self, param_list):
        self.nginx_conf.loadf(self.nginx_config_path)
        backup_result = defaultdict(dict)
        for param_name in param_list.keys():
            try:
                value = self.get_value(param_name = param_name)
                backup_result[param_name] = {"suc": True, "msg": value}
            except Exception as e:
                backup_result[param_name] = {"suc": False, "msg": "{}".format(e)}
                logger.error("'{}:{}' reading error: {}".format(self.domain_name, param_name, e))
                continue
        if os.path.exists(self.backup_file):
            return backup_result
        sysCommand("echo y | cp {} {}".format(self.nginx_config_path, self.backup_file))
        return backup_result
    
    def rollback(self):
        if not os.path.exists(self.backup_file):
            return
        
        sysCommand("echo y | cp {} {}".format(self.backup_file, self.nginx_config_path))
        sysCommand("systemctl restart nginx")
        os.remove(self.backup_file)


if __name__ == "__main__":
    handler = Nginx()
    handler.set_values({
        "gzip":{"value":"on"},
        "gzip_min_length":{"value":512},
        "gzip_buffers":{"value":"4 16k"},
        "gzip_comp_level":{"value":5},
        "gzip_vary":{"value":"on"},
        "gzip_disable":{"value":"msie6"},
        "gzip_proxied":{"value":"expired no-cache no-store private auth"},
        "gzip_types":{"value":"text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml"},
    })
    # handler.set_value("gzip", "on")
    # handler.set_value("gzip_min_length", 512)
    # handler.set_value("gzip_buffers", "4 16k")
    # handler.set_value("gzip_comp_level", 5)
    # handler.set_value("gzip_vary", "on")
    # handler.set_value("gzip_disable", "msie6")
    # handler.set_value("gzip_proxied", "expired no-cache no-store private auth")
    # handler.set_value("gzip_types", "text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml")